Skip to content

developer joy easter egg#312642

Merged
justschen merged 10 commits intomainfrom
justin/goomy
Apr 30, 2026
Merged

developer joy easter egg#312642
justschen merged 10 commits intomainfrom
justin/goomy

Conversation

@justschen
Copy link
Copy Markdown
Collaborator

@justschen justschen commented Apr 26, 2026

an easter egg :)

builds an svg structure, then animates with css!!!!

Copilot AI review requested due to automatic review settings April 26, 2026 23:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an “aquarium” animation overlay to the Agents window new-session screen, with a persistent toggle button in the chat bar and animated VS Code-logo “fish”/food interactions.

Changes:

  • Registers a new sessions workbench contribution that mounts an aquarium overlay and toggle button (AfterRestored).
  • Implements animated fish/food/water layers and supporting SVG generation + styling.
  • Adds supporting context key, i18n resource mapping, and stylelint allowlisting for new CSS variables.
Show a summary per file
File Description
src/vs/sessions/sessions.common.main.ts Loads the new aquarium contribution in the sessions workbench entrypoint.
src/vs/sessions/contrib/aquarium/browser/aquarium.contribution.ts Registers the aquarium workbench contribution and instantiates the overlay.
src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts Implements overlay lifecycle, DOM layers, interactions, and RAF-driven animation loop.
src/vs/sessions/contrib/aquarium/browser/fish.ts Builds fish DOM/SVG (strip-clipped VS Code logo) and shared SVG defs.
src/vs/sessions/contrib/aquarium/browser/media/aquarium.css Adds water/fish/food/toggle styling, animations, and reduced-motion handling.
src/vs/sessions/common/contextkeys.ts Introduces sessionsAquariumActive context key.
build/lib/stylelint/vscode-known-variables.json Allowlists new CSS custom properties used by the aquarium feature.
build/lib/i18n.resources.json Adds a sessions i18n project mapping for vs/sessions/contrib/aquarium.

Copilot's findings

Comments suppressed due to low confidence (1)

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:343

  • Food drops are wired to EventType.MOUSE_DOWN. On iOS this event doesn’t fire (pointer events are used), so users won’t be able to drop food. Please use addDisposableGenericMouseDownListener (or pointer events) for cross-platform input.
	store.add(addDisposableListener(mainContainer, EventType.MOUSE_DOWN, (e: MouseEvent) => {
		// Only spawn food on plain left clicks against background-ish surfaces.
		if (e.button !== 0) {
			return;
		}
  • Files reviewed: 8/8 changed files
  • Comments generated: 7

Comment thread build/lib/stylelint/vscode-known-variables.json Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/media/aquarium.css Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/fish.ts Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/fish.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Comments suppressed due to low confidence (3)

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:324

  • createActiveAquarium() mixes targetWindow.document (for fish) with the global document (for water, fishLayer, foodLayer). In multi-window scenarios this can create nodes in the wrong document/realm. Prefer consistently using targetWindow.document.createElement(...) here so the overlay is built in the same window as mainContainer.
	const water = document.createElement('div');
	water.className = 'agents-aquarium-water';
	// Insert as the FIRST child so all subsequent chat bar content paints over it.
	chatBar.insertBefore(water, chatBar.firstChild);
	store.add(toDisposable(() => water.remove()));

	const fishLayer = document.createElement('div');
	fishLayer.className = 'agents-aquarium-fish-layer';
	water.appendChild(fishLayer);

	const foodLayer = document.createElement('div');
	foodLayer.className = 'agents-aquarium-food-layer';
	water.appendChild(foodLayer);

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:470

  • spawnFood() uses the global document.createElement('div') even though this aquarium instance is scoped to targetWindow. For consistency (and to avoid cross-window DOM issues), use targetWindow.document.createElement(...) here too.
		const el = document.createElement('div');
		el.className = 'agents-aquarium-food';
		el.style.transform = `translate(${fx}px, ${fy}px)`;
		foodLayer.appendChild(el);
		food.push({ element: el, x: fx, y: fy, vy: randomBetween(20, 35) });

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:406

  • Disposing the shared SVG defs container as part of each aquarium instance's disposal is unsafe when there can be overlap (e.g., deactivate triggers an exit animation + delayed dispose, then activate creates a new aquarium before the delay completes). When the old instance disposes it will remove defs still referenced by the new fish. Consider ref-counting the shared defs (increment on activation, decrement on disposal) or tying defs lifetime to AquariumOverlay instead of the per-activation store.
		// Tear down the shared SVG defs container along with the last
		// active aquarium so we don't leak it across reloads.
		disposeSharedFishDefs();
	}));
  • Files reviewed: 8/8 changed files
  • Comments generated: 6

Comment thread src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts
Comment thread src/vs/sessions/contrib/aquarium/browser/fish.ts Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/media/aquarium.css Outdated
Comment thread build/lib/stylelint/vscode-known-variables.json Outdated
Comment thread src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Comments suppressed due to low confidence (4)

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:381

  • Listening for 'scroll' on targetWindow with { capture: true } will fire for scrolls anywhere in the document (since scroll doesn't bubble but is capturable). Calling getBoundingClientRect() in updateBounds() on every scroll can force frequent layouts and cause jank. Consider throttling this (e.g. schedule one updateBounds() per animation frame), or scope the listener to the specific scroll container(s) that can actually move the chat bar.
	// Listen on the main container so we always know cursor position even
	// when over the chat input (water has pointer-events:none).
	store.add(addDisposableListener(targetWindow, EventType.RESIZE, updateBounds, { passive: true }));
	store.add(addDisposableListener(targetWindow, 'scroll', updateBounds, { passive: true, capture: true }));

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:447

  • When prefers-reduced-motion is enabled, the RAF loop continues to schedule frames indefinitely even though updateFood/updateFish are skipped. This still consumes CPU/battery. Consider not starting the RAF loop at all (or stopping it) when reduceMotion is true, and keep the aquarium in its initial static state.
	const reduceMotion = targetWindow.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
	let lastFrame = performance.now();
	let rafDisposable: IDisposable | undefined;

	const tick = () => {
		const now = performance.now();
		const dtMs = Math.min(now - lastFrame, 100); // clamp big stalls
		const dt = dtMs / 1000;
		lastFrame = now;

		// Skip work when window is hidden (RAF stays alive lazily).
		const visible = targetWindow.document.visibilityState !== 'hidden';
		if (visible && !reduceMotion) {
			updateFood(dt);
			updateFish(dt);
		}

		rafDisposable = scheduleAtNextAnimationFrame(targetWindow, tick);
	};

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:291

  • The aquarium DOM is purely decorative, but the created elements (water, fish/food layers) are not marked as such. Add aria-hidden="true" (and/or role="presentation") to these containers (and likely individual fish/food elements) so they don't appear in the accessibility tree.
	const water = doc.createElement('div');
	water.className = 'agents-aquarium-water';
	// First child so subsequent chat bar content paints over it.
	chatBar.insertBefore(water, chatBar.firstChild);
	store.add(toDisposable(() => water.remove()));

	const fishLayer = doc.createElement('div');
	fishLayer.className = 'agents-aquarium-fish-layer';
	water.appendChild(fishLayer);

	const foodLayer = doc.createElement('div');
	foodLayer.className = 'agents-aquarium-food-layer';
	water.appendChild(foodLayer);

src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts:276

  • createActiveAquarium returns a no-op aquarium when the chat bar container isn't available/visible, but activate() still treats it as active (context key set, toggle shows active, and user preference may be persisted). This can leave the UI in an “enabled but nothing happens” state. Consider having createActiveAquarium return undefined (or throw a known error) when the chat bar isn't ready, and have activate() bail out without updating context/button state (optionally retry once the chat bar becomes visible).
	// Host inside the chat bar so chat input UI naturally paints on top —
	// no z-index gymnastics required.
	const chatBar = layoutService.getContainer(targetWindow, Parts.CHATBAR_PART);
	if (!chatBar || !layoutService.isVisible(Parts.CHATBAR_PART, targetWindow)) {
		return {
			dispose: () => store.dispose(),
			exit: () => { store.dispose(); return store; },
		};
	}
  • Files reviewed: 9/9 changed files
  • Comments generated: 2

Comment thread src/vs/sessions/contrib/aquarium/browser/aquariumOverlay.ts
Comment thread src/vs/sessions/contrib/aquarium/browser/fish.ts
@justschen justschen changed the title aquarium? developer joy easter egg Apr 30, 2026
Comment thread src/vs/sessions/contrib/aquarium/browser/fish.ts Outdated
@justschen justschen marked this pull request as ready for review April 30, 2026 06:06
@justschen justschen enabled auto-merge (squash) April 30, 2026 06:07
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

Screenshot Changes

Base: 1cd6969b Current: 63e13dc9

Changed (1)

sessions/sessionsPolicyBlocked/Loading/Dark
Before After
before after

@justschen justschen merged commit cba53f6 into main Apr 30, 2026
40 of 41 checks passed
@justschen justschen deleted the justin/goomy branch April 30, 2026 06:57
@vs-code-engineering vs-code-engineering Bot added this to the 1.119.0 milestone Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants